#include "shapes.h"
#include "canvas_terminal_impl.h"
#include <iostream>
#include <unistd.h>
#include <sys/ioctl.h>

using std::cout;

static inline void t_draw_dot(int x, int y, const char* style)
{
	//\033[<L>;<C>H
	cout << "\033[" << y << ';' << x << 'H' << style;
}

static inline void t_clear()
{
	cout << "\033[2J";
}

inline double CanvasTerminalImpl::v2pX(double vX)
{
	return vX / scaleW + 0.5;
}

inline double CanvasTerminalImpl::v2pY(double vY)
{
	return vY / scaleH + 0.5;
}

inline void CanvasTerminalImpl::putDot(double vX, double vY, const char* style)
{
	//cout << vY << '\n';
	if (vX <= width && vY <= height)
		t_draw_dot(v2pX(vX), v2pY(vY), style);
}

void CanvasTerminalImpl::getPhysInfo()
{
	struct winsize w;
	ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
	phys_height = w.ws_row;
	phys_width = w.ws_col;
}

void CanvasTerminalImpl::init()
{
	getPhysInfo();
	height = phys_height;
	width = phys_width;
	scaleH = height / phys_height;
	scaleW = width / phys_width;
}

void CanvasTerminalImpl::init(double h, double w)
{
	getPhysInfo();
	height = h;
	width = w;
	scaleH = height / phys_height;
	scaleW = width / phys_width;
}

void CanvasTerminalImpl::drawDot(Point* p, int _style)
{
	char style[] = { (char)_style, 0 };
	//cout << p->y << '\n';
	putDot(p->x, p->y, style);
}

void CanvasTerminalImpl::drawLine(Point* start, Point* end, int _style)
{
	double xs = start->x;
	double xe = end->x;
	double ys = start->y;
	double ye = end->y;
	char style[] = { (char)_style, 0 };
	if (xs - xe < 0.0001 && xs -xe > -0.0001) {
		bool b = ys > ye;
		double p = b ? -1 : 1;
		for (double y = ys; b == (y > ye); y += p*scaleH*0.99) {
			putDot(xs, y, style);
		}
	} else {
		double slope = (ys-ye) / (xs-xe);
		double x, y;
		bool b = xs > xe;
		double p = b ? -1 : 1;
		for (x = xs; b == (x > xe); x += p*scaleW*0.99) {
			y = ys + (x-xs)*slope;
			putDot(x, y, style);
		}
	}
}

void CanvasTerminalImpl::drawRectangle(Point *start, Point *end, int _style)
{
	double xs = start->x;
	double xe = end->x;
	double ys = start->y;
	double ye = end->y;
	const char *styleR, *styleC, *styleNW, *styleNE, *styleSW, *styleSE;
	if (_style == 0) {
		styleR = "─";
		styleC = "│";
		styleNW = styleNE = "┬";
		styleSW = styleSE = "┴";
	} else if (_style == '-' || _style == '|') {
		styleR = "-";
		styleC = "|";
		styleNW = styleNE = styleSW = styleSE = "+";
	} else {
		char buff[2] = { (char)_style, 0 };
		styleR = styleC =
		styleNW = styleNE =
		styleSW = styleSE = buff;
	}
	double xW, xE, yN, yS;
	if (xs > xe) {
		xW = xe;
		xE = xs;
	} else {
		xW = xs;
		xE = xe;
	}
	if (ys > ye) {
		yN = ye;
		yS = ys;
	} else {
		yN = ys;
		yS = ye;
	}
	for (double x = xW; x < xE; x += 0.99*scaleW)
		putDot(x, yN, styleR);
	for (double x = xW; x < xE; x += 0.99*scaleW)
		putDot(x, yS, styleR);
	for (double y = yN; y < yS; y += 0.99*scaleH)
		putDot(xW, y, styleC);
	for (double y = yN; y < yS; y += 0.99*scaleH)
		putDot(xE, y, styleC);
	putDot(xW, yN, styleNW);
	putDot(xE, yN, styleNE);
	putDot(xW, yS, styleSW);
	putDot(xE, yS, styleSE);
}

void CanvasTerminalImpl::clean()
{
	t_clear();
}
